package com.example.demo.core;

import com.example.demo.core.operator.IRuleOp;
import com.example.demo.core.operator.RuleOp;
import com.example.demo.dal.model.TbRule;
import com.example.demo.core.entity.Conclusion;
import com.example.demo.core.entity.Result;
import com.example.demo.core.entity.Rule;
import com.example.demo.core.enums.RuleOpEnum;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author zuitu@foxmail.com
 * @date 2020/06/18
 */
@Component
public class RuleFlow implements ApplicationListener<ContextRefreshedEvent> {
    private static final Map<String, IRuleOp> RULE_OP_MAP = new HashMap<>(16);

    public Result getResult(Map<String, String> facts, List<TbRule> ruleList) {
        List<Conclusion> hitList = new ArrayList<>();
        List<Conclusion> missList = new ArrayList<>();
        List<Rule> ruleHitList = new ArrayList<>();
        List<Rule> ruleMissList = new ArrayList<>();
        long i = 0;
        long j = 0;
        long m = 0;
        for (TbRule rule : ruleList) {
            j++;
            if (0 == rule.getLvl()) {
                long ruleCount = (rule.getRgt() - rule.getLft() + 1) / 2;
                i++;
                Conclusion conclusion = Conclusion.builder().name(rule.getName()).val(rule.getVal()).build();
                if (i == ruleCount) {
                    conclusion.setRuleList(new ArrayList<>(ruleHitList));
                    hitList.add(conclusion);
                } else {
                    conclusion.setRuleList(new ArrayList<>(ruleMissList));
                    missList.add(conclusion);
                }
                ruleHitList.clear();
                ruleMissList.clear();
                i = 0;
                j = 0;
                m = 0;
            } else {
                if (m > 0) {
                    continue;
                }
                if (i >= j) {
                    continue;
                }
                String myVal = facts.get(rule.getName());
                boolean status =  RULE_OP_MAP.get(rule.getOp()).doOp(myVal, rule.getVal());
                long subRuleCount = (rule.getRgt() - rule.getLft() + 1) / 2 + rule.getLvl() - 1;
                if (status) {
                    ruleHitList.add(Rule.builder().name(rule.getName()).val(rule.getVal()).op(rule.getOp()).build());
                    i += subRuleCount;
                } else {
                    ruleMissList.add(Rule.builder().name(rule.getName()).val(rule.getVal()).op(rule.getOp()).build());
                }
                if (!status && subRuleCount == (rule.getRgt() - rule.getLft() + 1) / 2) {
                    m = 1;
                }
            }
        }
        return Result.builder().missList(missList).hitList(hitList).build();
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        ApplicationContext applicationContext = contextRefreshedEvent.getApplicationContext();
        Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(RuleOp.class);
        beansWithAnnotation.forEach((key, value) -> {
            RuleOpEnum ruleOpEnum = value.getClass().getAnnotation(RuleOp.class).method();
            RULE_OP_MAP.put(ruleOpEnum.getOp(), (IRuleOp) value);
        });
    }
}
